﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="MulticastWeakDelegate.cs" company="JDML Consulting Ltd.">
//   Copyright 2011 Photon Project - http://photon.codeplex.com
// </copyright>
// <summary>
//   Defines the MulticastWeakDelegate type.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace Photon
{
    using System.Linq;
    using System.Threading;
    using Collections;

    public class MulticastWeakDelegate<TDelegate> : WeakDelegate<TDelegate> where TDelegate : class
    {
        #region Fields

        private volatile WeakDelegate<TDelegate>[] _invocationList;

        #endregion

        internal MulticastWeakDelegate(WeakDelegate<TDelegate>[] invocationList) : base(invocationList[0].Target, invocationList[0].Method)
        {
            _invocationList = invocationList;
        }

        public override sealed bool QueryIsAlive(bool flush)
        {
            if (flush)
            {
                return GetInvocationListCore(true).Length != 0;
            }

            return _invocationList != null ? _invocationList.Any(x => x.QueryIsAlive(false)) : false;
        }

        protected override sealed void OnInvokeAction()
        {
            foreach (IDelegatable delegatable in GetInvocationList(true))
            {
                delegatable.InvokeAction();
            }
        }

        protected override sealed void OnInvokeAction<TArg1>(TArg1 arg1)
        {
            foreach (IDelegatable delegatable in GetInvocationList(true))
            {
                delegatable.InvokeAction(arg1);
            }
        }

        protected override sealed void OnInvokeAction<TArg1, TArg2>(TArg1 arg1, TArg2 arg2)
        {
            foreach (IDelegatable delegatable in GetInvocationList(true))
            {
                delegatable.InvokeAction(arg1, arg2);
            }
        }

        protected override sealed void OnInvokeAction<TArg1, TArg2, TArg3>(TArg1 arg1, TArg2 arg2, TArg3 arg3)
        {
            foreach (IDelegatable delegatable in GetInvocationList(true))
            {
                delegatable.InvokeAction(arg1, arg2, arg3);
            }
        }

        protected override sealed void OnInvokeAction<TArg1, TArg2, TArg3, TArg4>(TArg1 arg1, TArg2 arg2, TArg3 arg3, TArg4 arg4)
        {
            foreach (IDelegatable delegatable in GetInvocationList(true))
            {
                delegatable.InvokeAction(arg1, arg2, arg3, arg4);
            }
        }

        public override void UseFallback(TDelegate value)
        {
            _invocationList[0].UseFallback(value);
        }

        protected override TReturn OnInvokeFunc<TReturn>()
        {
            var result = default(TReturn);
            foreach (IDelegatable delegatable in GetInvocationList(true))
            {
                result = delegatable.InvokeFunc<TReturn>();
            }

            return result;
        }

        protected override TReturn OnInvokeFunc<TArg1, TReturn>(TArg1 arg1)
        {
            var result = default(TReturn);
            foreach (IDelegatable delegatable in GetInvocationList(true))
            {
                result = delegatable.InvokeFunc<TArg1, TReturn>(arg1);
            }

            return result;
        }

        protected override TReturn OnInvokeFunc<TArg1, TArg2, TReturn>(TArg1 arg1, TArg2 arg2)
        {
            var result = default(TReturn);
            foreach (IDelegatable delegatable in GetInvocationList(true))
            {
                result = delegatable.InvokeFunc<TArg1, TArg2, TReturn>(arg1, arg2);
            }
            return result;
        }

        protected override TReturn OnInvokeFunc<TArg1, TArg2, TArg3, TReturn>(TArg1 arg1, TArg2 arg2, TArg3 arg3)
        {
            var result = default(TReturn);
            foreach (IDelegatable delegatable in GetInvocationList(true))
            {
                result = delegatable.InvokeFunc<TArg1, TArg2, TArg3, TReturn>(arg1, arg2, arg3);
            }
            return result;
        }

        protected override TReturn OnInvokeFunc<TArg1, TArg2, TArg3, TArg4, TReturn>(TArg1 arg1, TArg2 arg2, TArg3 arg3, TArg4 arg4)
        {
            var result = default(TReturn);
            foreach (IDelegatable delegatable in GetInvocationList(true))
            {
                result = delegatable.InvokeFunc<TArg1, TArg2, TArg3, TArg4, TReturn>(arg1, arg2, arg3, arg4);
            }
            return result;
        }

        protected override sealed WeakDelegate<TDelegate>[] GetTypedInvocationListCore(bool flush)
        {
            if (flush)
            {
                // copy current entries (for safe interlocked exchange later)
                var entries = _invocationList;
                if (entries != null)
                {
                    var newEntries = entries.Where(x => x.QueryIsAlive(false)).ToArray();
                    if (newEntries.Length != entries.Length)
                    {
                        if (newEntries.Length == 0)
                        {
                            newEntries = null;
                        }

#pragma warning disable 420

                        // replace entries
                        Interlocked.CompareExchange(ref _invocationList, newEntries, entries);

#pragma warning restore 420
                    }
                }
            }

            return _invocationList ?? EmptyArray<WeakDelegate<TDelegate>>.Instance;
        }
    }
}